﻿using System.Collections.Generic;
using System.Drawing;
using MossmanMill.View;
using System;

namespace MossmanMill.DataModel
{

    public class Card : Slottable
    {
        private static Pen wire = new Pen(Brushes.Maroon, 2);

        public int CardId { get; private set; } //Card_Id
        public int ModuleId { get; private set; } //Module_Id
        public string Name { get; private set; } //Module_Name
        public CardType Type { get; protected internal set; } //Module_Type
        public string Desc { get; private set; } //Module_Desc
        public string Identifier { get; set; } //e.g. WBDIM030503
        public Sensor SelectedSensor
        {
            get
            {
                if (SelectedIndex < 0 || SelectedIndex > channels) return null;
                return Sensors[SelectedIndex];
            }
        }

        public int SelectedIndex;
        private int channels;
        private Sensor[] _sensors;

        public Sensor[] Sensors //Module_Channels
        {
            get {
                if (_sensors != null) return _sensors;
                if (CardId == -1) return null; //for a new card, not in the database
                var db = DatabaseAccess.Instance;
                var items = db.GetSensors(this);
                int maxCh = 0;
                foreach (var item in items) maxCh = item.Channel > maxCh ? item.Channel+1 : maxCh;
                if (maxCh > channels) channels = maxCh;
                _sensors = new Sensor[channels];
                foreach (var sensor in items)
                {
                    _sensors[sensor.Channel] = sensor;
                }
                return _sensors;
            }
        }
       
        public Card (int slotId, int id, int rackId, int type, string name, string desc, int channels, int module, int pos)
        {
            SlotType = 'C';
            SlotId = slotId;
            CardId = id;
            RackId = rackId;
            Type = GetCardType(type);
            Name = name;
            Desc = desc;
            ModuleId = module;
            Position = pos;
            this.channels = channels;
            SelectedIndex = 0;
            
        }

        public int GetChannels()
        {
            return channels;
        }

        public int GetFreeChannels()
        {
            int count = 0;
            foreach (var sensor in Sensors)
            {
                if (sensor == null) count++;
            }
            return count;
        }

        public void SetSensors(Sensor[] sensors)
        {
            _sensors = sensors;
        }
        public void SetId(int id)
        {
            SlotId = SlotId;
        }

        public void SetCardId(int id)
        {
            CardId = id;
        }

        

        public override Bitmap GetDrawBitmap(Rectangle bounds, DrawType type)
        {
            var x = bounds.Left;
            var y = bounds.Top;
            var buffer = new Bitmap(bounds.Width, bounds.Height);
            var g = Graphics.FromImage(buffer);
            if (type == DrawType.Icon)
            {
                Drawing.FillOutlineRectangle(g, new Rectangle(0, 0, 32, 32), Brushes.LightGoldenrodYellow, Pens.Black);
                g.DrawString(this.Type.ToString(), Fonts.smallTimes, Brushes.BlueViolet, 12, 0);
                g.DrawString(this.GetChannels() + "CH", Fonts.smallTimes, Brushes.Blue, 0, 16);
                return buffer;
            }
            Rectangle sensorDrawRect = new Rectangle(new Point(100, 100), new Size(480 - (100), 80));
            var s = Sensors;
            Rectangle[] channelRects = new Rectangle[channels];
            for (int i = 0; i < channels; i++)
            {
                int u, v;
                if (i < channels / 2)
                {
                    u = 1;
                    v = i * 20 + 30;
                }
                else
                {
                    u = 59;
                    v = (i - channels / 2) * 20 + 30;
                }
                channelRects[i] = new Rectangle(u, v, 20, 10);
            }
            Rectangle cardDrawRect = new Rectangle(0, 0, 80, GetChannels() / 2 * 20 + 50);
            g.FillRectangle(Brushes.LightGoldenrodYellow, cardDrawRect); //Card Face Rectangle
            g.DrawRectangle(Pens.Black, cardDrawRect); //Card Face Rectange
            g.DrawString("I/O Module " + Name, Fonts.mediumArial, Brushes.Black, cardDrawRect.Right + 40, cardDrawRect.Top);                                    //Title Text
            g.DrawString(GetChannels() + " CH, " + Card.TypeToString(Type), Fonts.mediumArial, Brushes.Black, cardDrawRect.Right + 40, cardDrawRect.Top + 20);  //Channels
            g.DrawString(GetFreeChannels() + " Free Channels", Fonts.smallCourier, Brushes.Black, cardDrawRect.Right + 40, cardDrawRect.Top + 40);              //Free Channels

            g.DrawString(Name, Fonts.smallCourier, Brushes.Black, cardDrawRect.Location); //Card Model Name
            int numCh = GetChannels();
            for (int i = 0; i < numCh; i++)
            {
                g.FillRectangle(Brushes.Gold, channelRects[i]);
                g.DrawRectangle(Pens.DarkGoldenrod, channelRects[i]);
                bool left = i < numCh / 2;
                var rect = new Rectangle(channelRects[i].Left + (left ? 10 : 0), channelRects[i].Top, 10, 10);
                if (Sensors[i] != null && !Sensors[i].IsBlank()) g.FillEllipse(SelectedIndex == i ? Brushes.Red : Brushes.Black, rect);
                //else g.FillEllipse(Brushes.White, rect);
            }
            return buffer;
        }

        public override void draw(System.Drawing.Graphics graphics, int x, int y, float scale = 1.0f, bool drawChildren = false)
        {
            draw(graphics, new Rectangle(x, y, 81, GetChannels() / 2 * 20 + 51), scale, drawChildren);
        }

        public override void draw(System.Drawing.Graphics graphics, Rectangle bounds, float scale = 1.0f, bool drawChildren = false)
        {

            var x = bounds.Left;
            var y = bounds.Top;
            var buffer = GetDrawBitmap(bounds, DrawType.Full);
            graphics.DrawImage(buffer, x, y, buffer.Width * scale, buffer.Height * scale);
        }

        public override bool interact(Point loc, int type, int x, int y, float scale = 1.0f, bool interactWithChildren = false)
        {
            var s = Sensors;
            if (type == 2)
            {
                Rectangle[] channelRects = new Rectangle[channels];
                for (int i = 0; i < channels; i++)
                {
                    int u, v;
                    if (i < channels / 2)
                    {
                        u = 1;
                        v = i * 20 + 30;
                    }
                    else
                    {
                        u = 59;
                        v = (i - channels / 2) * 20 + 30;
                    }
                    channelRects[i] = new Rectangle(u + x, v + y, 20, 10);
                }
                for (int i = 0; i < channels; i++)
                {
                    if (Sensors[i] == null || Sensors[i].IsBlank()) continue;
                    if (channelRects[i].Contains(loc))
                    {
                        SelectedIndex = i;
                        return true;
                    }
                }
                if (new Rectangle(x, y, 80, GetChannels() / 2 * 20 + 50).Contains(loc)) return true;
            }
            if (interactWithChildren && SelectedSensor != null)
            {
                return SelectedSensor.interact(loc, type, x + 100, y + 100);
            }
            return false;
        }

        public bool Equals(Card card)
        {
            int c = 0;
            foreach (var sensor in Sensors)
            {
                if (sensor == null && card.Sensors[c] != null) return false;
                if (sensor != null)
                {
                    if (card.Sensors[c] == null) return false;
                    if (!sensor.Equals(card.Sensors[c])) return false;
                }
            }
            return base.Equals(card) &&
                card.Name.Equals(Name) &&
                card.Type == Type &&
                card.Desc.Equals(Desc) &&
                card.ModuleId == ModuleId;
        }

        public Card Copy()
        {
            return new Card(SlotId, CardId, RackId, TypeToInt(Type), Name, Desc, GetChannels(), ModuleId, Position);
        }

        public override string ToString()
        {
            return Name + " " + Type.ToString() + " " + GetChannels() + " Ch";
        }

        public override void Release()
        {
            base.Release();
            _sensors = null;
        }

        public override string[] GetInfo()
        {
            string[] str = new string[5];
            str[0] = "IO Module";
            str[1] = Name;
            str[2] = TypeToString(Type);
            str[3] = GetChannels() + " Ch";
            str[4] = Desc;
            return str;
        }

        public static int TypeToInt(CardType t)
        {
            switch (t)
            {
                case CardType.DO:
                    return 0;
                case CardType.DI:
                    return 1;
                case CardType.AO:
                    return 2;
                case CardType.AI:
                    return 3;
                default:
                    return 4;
            }
        }

        public static CardType GetCardType(int i)
        {
            switch (i)
            {
                case 0:
                    return CardType.DO;
                case 1:
                    return CardType.DI;
                case 2:
                    return CardType.AO;
                case 3:
                    return CardType.AI;
                case 4:
                    return CardType.OTHER;
            }
            return CardType.DO;
        }

        public static string TypeToString(CardType t)
        {
            switch (t)
            {
                case CardType.DO:
                    return "Digital Output";
                case CardType.DI:
                    return "Digital Input";
                case CardType.AO:
                    return "Analog Output";
                case CardType.AI:
                    return "Analog Input";
                default:
                    return "Other Type";
            }
        }
    }

    public enum CardType
    {
        DI,
        DO,
        AI,
        AO,
        OTHER
    }
}
